package com.core;

import com.annotation.After;
import com.annotation.Around;
import com.annotation.Before;
import com.annotation.Throwing;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description TODO
 * @Author 泥地里的蝴蝶
 * @Date 2024/5/10 20:54
 */
public class AopProxy implements MethodInterceptor {
    Object aopObject;
    Method beforeMethod;
    Method afterMethod;
    Method throwingMethod;
    Method aroundMethod;
    Map<Integer,Method> interceptorMethods=new HashMap<>();
    boolean isInterceptorAll=false;

    public AopProxy() {
    }

    public void setEnhancerMethods(Object aopObject){
        for (Method method : aopObject.getClass().getDeclaredMethods()) {
            if(method.isAnnotationPresent(Before.class)){
                beforeMethod=method;
            }else if(method.isAnnotationPresent(After.class)){
                afterMethod=method;
            }else if(method.isAnnotationPresent(Around.class)){
                aroundMethod=method;
            }else if(method.isAnnotationPresent(Throwing.class)){
                throwingMethod=method;
            }
        }
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        int hashCode = method.hashCode();
        if(isInterceptorAll||interceptorMethods.containsKey(hashCode)){
            JoinPoint joinPoint = new JoinPoint(method, objects);
            Object ret=null;
            try{
                invokeMethod(joinPoint,beforeMethod);
                if(aroundMethod!=null){
                    ret=invokeMethod(new ProceedingJoinPoint(o,methodProxy,method,objects),aroundMethod);
                }else{
                    ret=methodProxy.invokeSuper(o,objects);
                }
                return ret;
            }catch (Throwable throwable){
                throwable.printStackTrace();
                invokeMethod(throwable,throwingMethod);
                return ret;
            }finally {
                invokeMethod(joinPoint,afterMethod);
            }
        }
        return methodProxy.invokeSuper(o,objects);
    }
    public Object invokeMethod(Object joinPoint,Method method) throws Throwable{
        Object ret=null;
        if(method!=null){
            if(method.getParameterTypes().length>0){
                if(!method.getParameterTypes()[0].equals(joinPoint.getClass())){
                    throw new IllegalArgumentException("参数映射错误:非 IJoinPoint 参数");
                }
                ret=method.invoke(aopObject,joinPoint);
                return ret;
            }else{
                ret=method.invoke(aopObject);
                return aopObject;
            }
        }
        return null;
    }
}
